package com.xiaoyun.xpath.handler.field;

import cn.wanghaomiao.xpath.model.JXDocument;
import com.xiaoyun.xpath.HandlerUtils;

import java.lang.reflect.*;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class MapFieldHandler<T> extends ListFieldHandler<T> {


    @Override
    protected String getTypeName(ParameterizedType genericType) {
        Type[] types = genericType.getActualTypeArguments();
        return types[1].getTypeName();
    }

    @Override
    protected <T> void listMethodInvoke(Class<T> clazz, JXDocument jxDocument, T t, String fieldName, Class<?> clazzValue, String sonUrl, List<Object> listObj, String xpathMapping) throws Exception {
        String[] xpathMappingSub = xpathMapping.split(";");
        if (xpathMappingSub.length == 2) {
            List<Object> key = jxDocument.sel(xpathMappingSub[0]);
            List<Object> value = jxDocument.sel(xpathMappingSub[1]);

            int valueSize = value.size();
            int keySize = key.size();
            int max = Math.max(keySize, valueSize);

            Map<Object, Object> map = new LinkedHashMap<>();
            Field field = null;
            if (!"".equals(sonUrl) && sonUrl != null) {
                field = clazzValue.getDeclaredField(sonUrl);
            }
            if (field != null) {
                String methodNameUrl = HandlerUtils.getMethodName(sonUrl);
                Class<?> typeClass = Class.forName(field.getGenericType().getTypeName());
                Method methodUrl = clazzValue.getDeclaredMethod(methodNameUrl, typeClass);

                for (int i = 0; i < max; i++) {
                    Object itemStr;
                    Object keyValue;
                    if (i < valueSize) {
                        itemStr = value.get(i);
                    } else {
                        itemStr = new Object();
                    }
                    if (i < keySize) {
                        keyValue = key.get(i);
                    } else {
                        keyValue = new Object();
                    }

                    Object obj = clazzValue.getDeclaredConstructor().newInstance();
                    methodUrl.setAccessible(true);
                    methodUrl.invoke(obj, itemStr);
                    map.put(keyValue, obj);
                }
            } else {
                for (int i = 0; i < max; i++) {
                    Object itemStr;
                    Object keyValue;
                    if (i < valueSize) {
                        itemStr = value.get(i);
                    } else {
                        itemStr = new Object();
                    }
                    if (i < keySize) {
                        keyValue = key.get(i);
                    } else {
                        keyValue = new Object();
                    }
                    map.put(itemStr, keyValue);
                }
            }

            String methodName = HandlerUtils.getMethodName(fieldName);
            Method method = clazz.getDeclaredMethod(methodName, Map.class);
            method.setAccessible(true);
            method.invoke(t, map);
        }
    }


    @Override
    protected <T> void stringMethodInvoke(Class<T> clazz, JXDocument jxDocument, T t, String fieldName, String xpathMapping) throws Exception {
        String[] xpathMappingSub = xpathMapping.split(";");
        if (xpathMappingSub.length == 2) {
            List<Object> key = jxDocument.sel(xpathMappingSub[0]);
            List<Object> value = jxDocument.sel(xpathMappingSub[1]);
            int keySize = key.size();
            if (keySize == value.size()) {
                Map<Object, Object> map = new LinkedHashMap<>();
                for (int i = 0; i < keySize; i++) {
                    map.put(key.get(i), value.get(i));
                }
                String methodName = HandlerUtils.getMethodName(fieldName);
                Method method = clazz.getDeclaredMethod(methodName, Map.class);
                method.invoke(t, map);
            }
        }

    }

    @Override
    public String getType() {
        return Map.class.getName();
    }
}
